#include <graphics/Engine.h>

namespace ne
{
    template<> Engine* Singleton<Engine>::mpInstance = 0;

    Engine::Engine()
        : mpScreen(0),
          mpTimer(0),
          mpMouse(0),
          mpKeyboard(0),
          mpPerspectiveCamera(0),
          mpPerspectiveViewport(0),
          mpOrthographicCamera(0),
          mpOrthographicViewport(0),
          mpRenderSystem(0),
          mpBufferObjectManager(0),
          mpGLSLManager(0),
          mpImageManager(0),
          mpTextureManager(0),
          mpMaterialManager(0),
          mpFontManager(0),
          mpMeshManager(0),
          mpSkeletonManager(0),
          mpAnimationManager(0),
          mpModelManager(0),
          //mpUIManager(0),
          mpFont(0),
          mIsReady(false)
    {
        Log::DefaultLog.open();

        mpScreen = new Screen();

        mpTimer = new Timer();
        mpTimer->reset();

        mpMouse = new Mouse();
        mpMouse->reset();

        mpKeyboard = new Keyboard();
        mpKeyboard->reset();
    }

    bool Engine::initialize()
    {
        mpRenderSystem = new RenderSystem();
        if (!mpRenderSystem->initialize())
        {
            Log::Out(LL_ERROR, "Initialize RenderSystem [Failed]");
            return false;
        }

        mpPerspectiveCamera = new Camera();
        mpPerspectiveCamera->setProjectionType(PT_PERSPECTIVE);
        mpPerspectiveCamera->setPosition(Vector3::UNIT_Z);
        mpPerspectiveViewport = new Viewport(mpPerspectiveCamera);

        mpOrthographicCamera = new Camera();
        mpOrthographicCamera->setProjectionType(PT_ORTHOGRAPHIC);
        mpOrthographicCamera->setPosition(Vector3::UNIT_Z);
        mpOrthographicViewport = new Viewport(mpOrthographicCamera);

        mpBufferObjectManager = new GLBufferObjectManager();
        mpGLSLManager = new GLSLManager();
        mpImageManager = new ImageManager();
        mpTextureManager = new TextureManager();
        mpMaterialManager = new MaterialManager();
        mpFontManager = new FontManager();
        mpMeshManager = new MeshManager();
        mpSkeletonManager = new SkeletonManager();
        mpAnimationManager = new AnimationManager();
        mpModelManager = new ModelManager();
        //mpUIManager = new UIManager();

#if NE_PLATFORM == NE_PLATFORM_WINDOWS
        mpFont = mpFontManager->createTrueTypeFont("EngineFont", "c:/windows/fonts/cour.ttf", 12, FHT_MONO);
#elif NE_PLATFORM == NE_PLATFORM_MACOSX
        mpFont = mpFontManager->createTrueTypeFont("EngineFont", "/Library/Fonts/Courier New Bold.ttf", 14, FHT_LIGHT);
#else
        mpFont = mpFontManager->createTrueTypeFont("EngineFont", "../font/cour.ttf", 12, FHT_NORMAL);
#endif

        if (mpFont.isNull())
        {
            Log::Out(LL_ERROR, "Initialize EngineFont [Failed]");
        }
        mpFont->load();

        Log::Out("Construct NuclearEngine [Success]");

        mIsReady = true;

        return true;
    }

    bool Engine::isReady() const
    {
        return mIsReady;
    }

    void Engine::setWorkSpace(const std::string &space)
    {
        mWorkSpace = space;
        if (mWorkSpace.length())
        {
            if (mWorkSpace[mWorkSpace.length() - 1] != '/')
            {
                mWorkSpace += "/";
            }
        }
    }

    void Engine::setResolution(const uint32_t width, const uint32_t height)
    {
        mpScreen->setResolution(width, height);
        mpPerspectiveViewport->resize(width, height);
        mpOrthographicViewport->resize(width, height);
    }

    void Engine::setFullScreen(const bool b)
    {
        mpScreen->setFullScreen(b);
    }

    void Engine::setMousePosition(const real x, const real y)
    {
        mpMouse->setPosition(x, y);
        //mpUIManager->setMousePosition(x, y);
        MouseEvent event(*mpMouse);
    }

    void Engine::setMouseButtonUp(const MouseButton &button)
    {
        mpMouse->setButtonUp(button);
        //mpUIManager->setMouseButtonUp(button);
        MouseEvent event(*mpMouse);
    }

    void Engine::setMouseButtonDown(const MouseButton &button)
    {
        mpMouse->setButtonDown(button);
        //mpUIManager->setMouseButtonDown(button);
        MouseEvent event(*mpMouse);
    }

    void Engine::setMouseWheelVariation(const real v)
    {
        mpMouse->setWheelVariation(v);
        //mpUIManager->setMouseWheelVariation(v);
        MouseEvent event(*mpMouse);
    }

    void Engine::setKeyboardModifierUp(const KeyboardModifier &km)
    {
        mpKeyboard->setModifierUp(km);
        //mpUIManager->setKeyboardModifierUp(km);
        KeyboardEvent event(*mpKeyboard, KK_UNLABELED);
    }

    void Engine::setKeyboardModifierDown(const KeyboardModifier &km)
    {
        mpKeyboard->setModifierDown(km);
        //mpUIManager->setKeyboardModifierDown(km);
        KeyboardEvent event(*mpKeyboard, KK_UNLABELED);
    }

    void Engine::setKeyboardTranslationMode(const KeyboardTranslationMode &ktm)
    {
        mpKeyboard->setTranslationMode(ktm);
        //mpUIManager->setKeyboardTranslationMode(ktm);
        KeyboardEvent event(*mpKeyboard, KK_UNLABELED);
    }

    void Engine::setKeyboardKeyUp(const KeyboardKey &key)
    {
        mpKeyboard->setKeyUp(key);
        //mpUIManager->setKeyboardKeyUp(key);
        KeyboardEvent event(*mpKeyboard, key);
    }

    void Engine::setKeyboardKeyDown(const KeyboardKey &key)
    {
        mpKeyboard->setKeyDown(key);
        //mpUIManager->setKeyboardKeyDown(key);
        KeyboardEvent event(*mpKeyboard, key);
    }

    void Engine::setCharacter(const uint32_t character)
    {
        //mpUIManager->setCharacter(character);
    }

    Engine::~Engine()
    {
        mpFont.setNull();

        //delete mpUIManager;

        // Delete ModelManager before SkeletonManager
        delete mpModelManager;
        delete mpAnimationManager;
        delete mpSkeletonManager;
        delete mpMeshManager;

        // Delete FontManager before MaterialManager
        delete mpFontManager;
        // Delete MaterialManager before TextureManager and ImageManager
        delete mpMaterialManager;
        delete mpTextureManager;
        delete mpImageManager;
        delete mpGLSLManager;
        delete mpBufferObjectManager;

        delete mpOrthographicViewport;
        delete mpOrthographicCamera;
        delete mpPerspectiveViewport;
        delete mpPerspectiveCamera;

        delete mpRenderSystem;

        delete mpKeyboard;
        delete mpMouse;
        delete mpTimer;
        delete mpScreen;

        Log::Out("Disconstruct NuclearEngine [Success]");
        Log::DefaultLog.close();
    }

    Engine& Engine::GetSingleton()
    {
        assert(mpInstance);
        return *mpInstance;
    }

    Engine* Engine::GetSingletonPtr()
    {
        return mpInstance;
    }
}
